module arm.dma;

/**
* https://hui-shao.cn/stm32-adc-dma-continue/
*/

import chip;


/// 判定有对应总线外设
private enum bool hasBusPeriph = HasPeriph!("DMA1","DMA2","DMA3");
static if(hasBusPeriph):
import arm.bus;
import arm.rcc;
import arm.nvic;
import arm.volatile;




static interface DMA(alias mPeri) //: MMIOBus!(mPeri)
{
    /// 通道
    enum FlagChannel
    {
        /// 通道0
        CH0 = 0b000,
        /// 通道1
        CH1 = 0b001,
        /// 通道2
        CH2 = 0b010,
        /// 通道3
        CH3 = 0b011,
        /// 通道4
        CH4 = 0b100,
        /// 通道5
        CH5 = 0b101,
        /// 通道6
        CH6 = 0b110,
        /// 通道7
        CH7 = 0b111,
    }
    /// DMA方向
    enum FlagDirection
    {
        ///Periph2Mem = 0b00,
        PeripheralToMemory = 0b00,      /// 从外设到内存
        /// 从内存到外设
        MemoryToPeripheral = 0b01,
        /// 从外设到内存
        MemoryToMemory = 0b10,
    }
    /// DMA burst模式
    enum FlagBurst
    {
        /// 1个传输
        Single = 0b00,
        /// 4个传输
        Inc4 = 0b01,
        /// 8个传输
        Inc8 = 0b10,
        /// 16个传输
        Inc16 = 0b11,
    }
    /// DMA 优先级
    enum FlagPriority
    {
        /// 低
        Low = 0b00,
        /// 中
        Medium = 0b01,
        /// 高
        High = 0b10,
        /// 非常高
        VeryHigh = 0b11,
    }
    /// DMA 大小
    enum FlagSize
    {
        /// 8位
        Byte = 0b00,
        /// 16位
        HalfWord = 0b01,
        /// 32位
        Word = 0b10,
    }
    enum FlagFifoStatus
    {
        /// FIFO空
        Empty = 0b100,
        /// FIFO满
        Full = 0b101,
        /// FIFO < 1/4
        Less1_4 = 0b000,
        /// FIFO > 1/4 and FIFO < 1/2
        Less1_2 = 0b001,
        /// FIFO > 1/2 and FIFO < 3/4
        Less3_4 = 0b010,
        /// FIFO > 3/4 and FIFO < full
        LessFull = 0b011,
    }

    /// Stream name
    enum FlagStream
    {
        /// Stream 0
        Stream0 = 0,
        /// Stream 1
        Stream1 = 1,
        /// Stream 2
        Stream2 = 2,
        /// Stream 3
        Stream3 = 3,
        /// Stream 4
        Stream4 = 4,
        /// Stream 5
        Stream5 = 5,
        /// Stream 6
        Stream6 = 6,
        /// Stream 7
        Stream7 = 7,
    }

    struct Stream_CR_Type{
        uint EN : 1;                /// 使能
        uint DMEIE : 1;             /// 目标地址错误中断
        uint TEIE : 1;              /// 传输错误中断
        uint HTIE : 1;              /// 传输半完成中断
        uint TCIE : 1;              /// 传输完成中断
        uint PFCTRL : 1;            /// 外设流控制
        FlagDirection DIR : 2;      /// 数据传输方向
        uint CIRC : 1;              /// 循环模式
        uint PINC : 1;              /// 外设地址自增
        uint MINC : 1;              /// 内存地址自增
        FlagSize PSIZE : 2;         /// 外设数据大小
        FlagSize MSIZE : 2;         /// 内存数据大小
        uint PINCOS : 1;            /// 外设地址偏移大小
        uint PL : 2;                /// 优先级
        uint DBM : 1;               /// 双缓冲模式
        uint CT : 1;                /// 当前传输
        uint :2;                    /// 保留
        FlagBurst PBURST : 2;       /// 外设突发传输
        FlagBurst MBURST : 2;       /// 内存突发传输
        FlagChannel CHSEL : 3;           /// 通道选择

        ref T opCast(T:uint)() const
        {
            return *cast(T*)&this;
        }
    }
    struct Stream_NDTR_Type
    {
        uint NDT : 16;              /// 传输数据量
        uint : 16;                  /// 保留
    }
    struct Stream_PAR_Type
    {
        uint PA : 32;               /// 外设地址
    }
    struct Stream_M0AR_Type
    {
        uint M0A : 32;              /// 内存地址
    }
    struct Stream_M1AR_Type
    {
        uint M1A : 32;              /// 内存地址
    }
    struct Stream_FCR_Type
    {
        uint FTH : 2;               /// FIFO阈值
        uint DMDIS:1;               /// 目标地址突发模式
        uint FS : 3;                /// FIFO状态
        uint :1;                    /// 保留
        uint FEIE : 1;              /// FIFO错误中断


        ref T opCast(T:uint)() const
        {
            return *cast(T*)&this;
        }
    }
    struct Stream_Type
    {
        volatile!uint   CR;        /// 通道配置
        volatile!uint   NDTR;      /// 传输数据量
        volatile!uint   PAR;       /// 外设地址
        volatile!uint   M0AR;      /// 内存地址
        volatile!uint   M1AR;      /// 内存地址
        volatile!uint   FCR;       /// FIFO配置        
    }

    private
    {
        version(NONE) enum DMA1_Type* mBase = Peripherals.DMA1;
        else enum mBase = mPeri;
        enum IFName = __traits(identifier, mPeri);
    }
    /// 通道
    final abstract class Stream(FlagStream mfs) //if( (mStream >= FlagStream.min) && ( mStream <= FlagStream.min) )
    {
        import arm.misc:combineStr;
        //enum mIrq = Irq;
        
        enum string mfsstr = mfs.stringof;

        //pragma(msg, "K:",mfsstr);
        //pragma(msg, "K:",typeof(this.outer));
        //pragma(msg, "K:",typeof(this));
        //pragma(msg, "K:",__traits(hasMember,this.outer.FlagStream,"Stream0"));
        
        /// 配置寄存器偏移位置
        enum StreamOffset = Stream_CR_Type.sizeof * mfs;
        /// 通道寄存器
        enum Stream_Type* mStream = cast(Stream_Type*) (cast(size_t)(&mBase.S0CR) + StreamOffset);
        /// Stream 对应中断名称
        enum IRQName = combineStr!(IFName,"_Stream",(cast(int)mfs).stringof);
        /// Stream 对应中断
        enum mInterrupt = __traits(getMember,mBase.Interrupt,IRQName);

        package{
            alias IRQCallType = typeof(this);
            alias IRQHandler = void function(IRQCallType);
            /// 中断服务回调函数
            __gshared static IRQHandler IRQCallback;
            /// 默认中断服务
            void HookIrqHandler() 
            {
                //alias s1 = Stream!(Stream!mfs);

                if(IRQCallback !is null) {
                    IRQCallback(this);
                }
                // 重置所有可能的中断位
            }
        }

        
        //enum Stream_name = mIrqStr.split(",");

        //pragma(msg, "K:",mIrqStr);
        //enum ss = 1;
        //pragma(msg, "K:",ss.stringof);
        //pragma(msg,mIrqStr);

        //alias msCR = mBase.S0CR;
        
        
        /// 控制寄存器
        static void CR(Stream_CR_Type cr) @property
        {
            //cr.CHSEL = mfc;
            mStream.CR = cast(uint)cr;
        }
        static immutable(Stream_CR_Type) CR() @property 
        {
            return cast(immutable(Stream_CR_Type))mStream.CR;
        }

        /// 设定数据量
        static void DataCount(uint count) @property
        in(count <= ushort.max)
        {
            mStream.NDTR = count;
        }
        static uint DataCount() @property
        {
            return mStream.NDTR;
        }
        /// 设定外设地址
        static void PeripheralAddress(void* ptr) @property
        {
            mStream.PAR = cast(size_t)ptr;
        }
        static immutable(void*) PeripheralAddress() @property 
        {
            
            return cast(immutable(void*))mStream.PAR;
        }
        /// 设定内存地址
        static void MemoryAddress0(void* ptr) @property
        {
            mStream.M0AR = cast(size_t)ptr;
        }
        static void* MemoryAddress0() @property
        {
            return cast(void*)mStream.M0AR;
        }
        /// 设定内存地址
        static void MemoryAddress1(void* ptr) @property
        {
            mStream.M1AR = cast(size_t)ptr;
        }
        static void* MemoryAddress1() @property
        {
            return cast(void*)mStream.M1AR;
        }
        /// FIFO 控制寄存器
        static void FCR(Stream_FCR_Type fcr) @property
        {
            mStream.FCR = cast(uint)fcr;
        }
        static immutable(Stream_FCR_Type) FCR() @property
        {
            return cast(immutable(Stream_FCR_Type))mStream.FCR;
        }
        /// FIFO 状态
        static  immutable(FlagFifoStatus) FCRStatus() @property
        {
            return cast(immutable(FlagFifoStatus))FCR.FS;
        }
        /// 设定 Stream 工作参数
        static void Config(Stream_CR_Type conf,void* src,void* dst,uint count)
        {
            //CR(conf);
            DataCount(count);
            if(conf.DIR == FlagDirection.MemoryToPeripheral){
                PeripheralAddress = dst;
                MemoryAddress0 = src;
            }else{
                PeripheralAddress = src;
                MemoryAddress0 = dst;
            }

        }

        /// 设定 peripheral to memory 模式
        static void PeripheralToMemory(void *src,void *dst,ushort count)
        {
            Stream_CR_Type mCR = this.CR;
            mCR.DIR = FlagDirection.PeripheralToMemory;
            Config(mCR,src,dst,count);

            //this.CR = mCR;
        }
        /// 设定 memory to peripheral 模式
        /// 设定 memory to memory 模式
        static void MemoryToMemory(void* src,void* dst,uint count)
        {
            Stream_CR_Type mCR = this.CR;
            mCR.DIR = FlagDirection.MemoryToMemory;
            //this.CR = mCR;
            Config(mCR,src,dst,count);
        }

        /// 放弃使用 Stream
        static void DeInit()
        {
            CR = Stream_CR_Type.init;
            DataCount = 0;
            PeripheralAddress = null;
            MemoryAddress0 = null;
            MemoryAddress1 = null;
            FCR = cast(Stream_FCR_Type)0x21;
        }

    }
    /// 使能
    static void Enable()
    {
        RCC.Power!(IFName~"EN")(1);
    }
    /// 禁止
    static void Disable()
    {
        RCC.Power!(IFName~"EN")(0);
    }


    pragma(crt_constructor,CrtPriority!(FlagCrt.Other,0))
    static this()
    {
        RCC.Power!(IFName~"EN")(1);
    }

}

alias DMA1 = DMA!(Peripherals.DMA1);

enum DMA1.Stream_CR_Type fdf = {
    CHSEL:DMA1.FlagChannel.CH0
    };

alias c1 = DMA1.Stream!(DMA1.FlagStream.Stream0);

import arm.nvic;


enum IRQHandler ccb ={
    //DMA1.Enable();
    
};
alias c2 = CreateHandler!(cast(IRQn_Ext)c1.mInterrupt,ccb);
//NVIC.C






//pragma(msg, "K:",cast(size_t)c1);